Az S&P 500 egy tőzsdei index, amely az Egyesült Államok 500 legnagyobb, nyilvánosan forgalmazott vállalatának teljesítményét méri. Ezeket a vállalatokat a Standard & Poor’s nevű pénzügyi szolgáltató cég válogatja össze különböző szempontok alapján (pl. piaci kapitalizáció, likviditás, szektorális reprezentáció stb.).
Könyvtárak telepítése
Függvények beolvasása
get_clean_financial_data <- function(symbol = "^GSPC",
from = "2010-01-01",
to = Sys.Date(),
periodicity = "daily") {
# Adatok lekérése Yahoo Finance-ből
getSymbols(symbol, src = "yahoo", from = from, to = to,
periodicity = periodicity, auto.assign = FALSE) -> raw_data
# NA értékek eltávolítása
raw_data <- na.omit(raw_data)
# Záróár kinyerése (Close oszlop)
close_prices <- Cl(raw_data)
# Loghozam számítása
log_returns <- log(close_prices) %>% na.omit()
# Visszatérés listaként
return(list(
data= raw_data,
price = close_prices,
log_return = log_returns
))
}
make_diff_series <- function(series){
diff_series <- diff(series) %>% na.omit()
return(diff_series)
}
get_basic_stats <- function(data){
# Átlagos loghozam számítása
mean_ret <- mean(data$log_return)
# Szórás (standard deviation): a loghozamok volatilitását (kockázatát) méri. Minél nagyobb, annál ingadozóbb az időszak.
sd_ret <- sd(data$log_return)
# Ferdeség (skewness): az eloszlás aszimmetriáját mutatja:
# Pozitív érték: hosszabb jobb oldali farok → több extrém pozitív hozam.
# Negatív érték: hosszabb bal oldali farok → nagyobb veszteségek gyakorisága.
skew_ret <- skewness(data$log_return)
# Csúcsosság (kurtosis): az eloszlás "csúcsosságát" vagy "leptokurtikusságát" mutatja:
# Értéke 3 körül → normáleloszlás
kurt_ret <- kurtosis(data$log_return)
cat("Átlagos napi loghozam: ", round(mean_ret, 6), "\n")
cat("Szórás: ", round(sd_ret, 6), "\n")
cat("Ferdeség (skewness): ", round(skew_ret, 3), "\n")
cat("Csúcsosság (kurtosis): ", round(kurt_ret, 3), "\n")
return(list(mean_ret, sd_ret, skew_ret, kurt_ret))
}
KPSS azt vizsgálja, hogy egy idősor trend-stacionárius-e, vagy nem-stacionárius (azaz egységgyökös). A trend-stacionárius sorban van determinisztikus trend (pl. lineáris növekedés), de a reziduumok stacionáriusak. Ezzel szemben a nem-stacionárius sorban a trend stochasztikus (random séta jelleg).
- Ha a KPSS statisztika nagyobb, mint a kritikus érték → elutasítjuk a stacionaritás hipotézisét.
- Ha kisebb → nem tudjuk elutasítani → a sor trend-stacionárius.
check_stationarity <- function(series) {
if (!("xts" %in% class(series))) {
stop("A bemenetnek xts objektumnak kell lennie.")
}
cat("Stacionaritás vizsgálat loghozamra:\n")
adf_result <- adf.test(series)
cat("\n--- Augmented Dickey-Fuller teszt ---\n")
print(adf_result)
if (adf_result$p.value < 0.05) {
cat("✅ ADF szerint a sor stacionárius.\n")
} else {
cat("❌ ADF szerint a sor nem stacionárius.\n")
}
kpss_result <- ur.kpss(series)
cat("\n--- KPSS teszt ---\n")
print(summary(kpss_result))
if (kpss_result@teststat > kpss_result@cval[1]) {
cat("❌ KPSS szerint a sor nem stacionárius.\n")
} else {
cat("✅ KPSS szerint a sor stacionárius.\n")
}
cat("\nℹ️ Javaslat: Ha az egyik teszt szerint nem stacionárius, differenciálni érdemes.\n")
}
- ACF, PACF diagnosztika megjelenítése
visual_stationarity_diagnostics <- function(series, window = 100, name = "Idősor") {
if (!("xts" %in% class(series))) {
stop("A bemenetnek xts objektumnak kell lennie.")
}
# Alapbeállítások visszaállítása a végén
old_par <- par(no.readonly = TRUE)
on.exit(par(old_par))
# Kétsoros ábra
par(mfrow = c(1, 2),
mar = c(4.2, 4.2, 3.5, 1), # Margók: alul, bal, fent, jobb
oma = c(0, 0, 2, 0), # Külső margók a főcímhez
cex.main = 1.1, # Főcím méret
cex.lab = 1, # Tengelyfelirat méret
cex.axis = 0.9) # Tengelyskála méret
# 1. ACF
acf(series, main = paste("ACF -", name), lag.max = 30)
# 2 PACF
pacf(series, main = paste("PACF -", name), lag.max = 30)
}
## Átlagos napi loghozam: 8.279554
## Szórás: 0.21674
## Ferdeség (skewness): -0.127
## Csúcsosság (kurtosis): -0.758
## Stacionaritás vizsgálat loghozamra:
##
## --- Augmented Dickey-Fuller teszt ---
##
## Augmented Dickey-Fuller Test
##
## data: series
## Dickey-Fuller = -2.5813, Lag order = 11, p-value = 0.3322
## alternative hypothesis: stationary
##
## ❌ ADF szerint a sor nem stacionárius.
##
## --- KPSS teszt ---
##
## #######################
## # KPSS Unit Root Test #
## #######################
##
## Test is of type: mu with 7 lags.
##
## Value of test-statistic is: 15.1809
##
## Critical value for a significance level of:
## 10pct 5pct 2.5pct 1pct
## critical values 0.347 0.463 0.574 0.739
##
## ❌ KPSS szerint a sor nem stacionárius.
##
## ℹ️ Javaslat: Ha az egyik teszt szerint nem stacionárius, differenciálni érdemes.
diff_log_returns <- make_diff_series(data$log_return)
## Stacionaritás vizsgálat loghozamra:
## Warning in adf.test(series): p-value smaller than printed p-value
##
## --- Augmented Dickey-Fuller teszt ---
##
## Augmented Dickey-Fuller Test
##
## data: series
## Dickey-Fuller = -10.714, Lag order = 11, p-value = 0.01
## alternative hypothesis: stationary
##
## ✅ ADF szerint a sor stacionárius.
##
## --- KPSS teszt ---
##
## #######################
## # KPSS Unit Root Test #
## #######################
##
## Test is of type: mu with 7 lags.
##
## Value of test-statistic is: 0.0594
##
## Critical value for a significance level of:
## 10pct 5pct 2.5pct 1pct
## critical values 0.347 0.463 0.574 0.739
##
## ✅ KPSS szerint a sor stacionárius.
##
## ℹ️ Javaslat: Ha az egyik teszt szerint nem stacionárius, differenciálni érdemes.
Az ARIMA modell három komponensből áll: - AR (p): autoregresszív tag — a sor saját elmúlt értékein alapul - I (d): integrált tag — hányadik differenciálás szükséges a stacionaritáshoz - MA (q): mozgóátlag tag — hibatagok elmúlt értékein alapul
Az ACF megmutatja, hogy az idősor aktuális értéke milyen mértékben korrelál a korábbi értékekkel (lag-ekkel).
A PACF azt méri, hogy egy adott lag mekkora hatással van a jelenre, ha a köztes lag-ek hatását kiszűrjük.
fit1 <- Arima(diff_log_returns, order = c(2, 0, 1))
fit2 <- Arima(diff_log_returns, order = c(3, 0, 1))
summary(fit1)
## Series: diff_log_returns
## ARIMA(2,0,1) with non-zero mean
##
## Coefficients:
## ar1 ar2 ma1 mean
## -0.1274 0.0981 -0.0308 6e-04
## s.e. 0.1740 0.0389 0.1737 3e-04
##
## sigma^2 = 0.0001562: log likelihood = 4473.32
## AIC=-8936.63 AICc=-8936.59 BIC=-8910.04
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set -2.812685e-06 0.01248311 0.008346342 121.2745 170.708 0.6823342
## ACF1
## Training set 0.0003934224
summary(fit2)
## Series: diff_log_returns
## ARIMA(3,0,1) with non-zero mean
##
## Coefficients:
## ar1 ar2 ar3 ma1 mean
## 0.4451 0.1846 -0.0753 -0.6015 6e-04
## s.e. 0.2845 0.0535 0.0332 0.2841 3e-04
##
## sigma^2 = 0.0001563: log likelihood = 4473.75
## AIC=-8935.51 AICc=-8935.45 BIC=-8903.59
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set -2.329628e-06 0.01247948 0.008340478 115.8807 165.6071 0.6818548
## ACF1
## Training set -0.00271077
## fit1 = -8936.632
## fit2 = -8935.508
##
## Ljung-Box test
##
## data: Residuals from ARIMA(2,0,1) with non-zero mean
## Q* = 95.585, df = 7, p-value < 2.2e-16
##
## Model df: 3. Total lags used: 10
## 'log Lik.' 4473.316 (df=5)
## [1] -8910.036
fit3 <- Arima(diff_log_returns, order = c(2, 0, 2))
fit4 <- Arima(diff_log_returns, order = c(3, 0, 2))
fit5 <- Arima(diff_log_returns, order = c(4, 0, 2))
| Modell | AIC | BIC | σ² | LLF | RMSE | MAE | ACF1 |
|---|---|---|---|---|---|---|---|
| ARIMA(2,0,2) | -9553.86 ✅ | -9521.55 ✅ | 0.0001555 ✅ | 4782.93 | 0.01245 ✅ | 0.00843 ✅ | 0.01819 |
| ARIMA(3,0,2) | -9482.92 | -9445.22 | 0.0001624 | 4748.46 | 0.01272 | 0.00846 | 0.00203 |
| ARIMA(4,0,2) | -9551.06 | -9507.98 | 0.0001555 | 4783.53 ✅ | 0.01244 | 0.00843 | 0.000046 ✅ |
## Series: diff_log_returns
## ARIMA(2,0,2) with non-zero mean
##
## Coefficients:
## ar1 ar2 ma1 ma2 mean
## -1.7429 -0.8804 1.6228 0.7332 6e-04
## s.e. 0.0264 0.0261 0.0375 0.0368 3e-04
##
## sigma^2 = 0.0001471: log likelihood = 4518.85
## AIC=-9025.71 AICc=-9025.65 BIC=-8993.79
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set -2.592903e-06 0.01211035 0.008287736 120.8726 185.2746 0.677543
## ACF1
## Training set 0.01957427
## Series: diff_log_returns
## ARIMA(3,0,2) with non-zero mean
##
## Coefficients:
## ar1 ar2 ar3 ma1 ma2 mean
## 0.8217 -0.3434 -0.1826 -0.9845 0.5935 6e-04
## s.e. 0.1772 0.1368 0.0323 0.1788 0.1399 3e-04
##
## sigma^2 = 0.0001557: log likelihood = 4476.75
## AIC=-8939.5 AICc=-8939.43 BIC=-8902.27
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set -3.369299e-06 0.01245461 0.008349034 117.1651 180.4543 0.6825542
## ACF1
## Training set 0.0026347
## Series: diff_log_returns
## ARIMA(4,0,2) with non-zero mean
##
## Coefficients:
## ar1 ar2 ar3 ar4 ma1 ma2 mean
## -1.6679 -0.7854 0.0210 -0.0149 1.5673 0.6866 6e-04
## s.e. 0.0725 0.0844 0.0522 0.0347 0.0679 0.0610 3e-04
##
## sigma^2 = 0.0001472: log likelihood = 4519.58
## AIC=-9023.16 AICc=-9023.06 BIC=-8980.6
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set -3.769517e-06 0.0121045 0.008287503 120.3982 184.6049 0.6775239
## ACF1
## Training set 0.0006863117
##
## Ljung-Box test
##
## data: Residuals from ARIMA(2,0,2) with non-zero mean
## Q* = 16.06, df = 6, p-value = 0.01343
##
## Model df: 4. Total lags used: 10
A pénzügyi idősorok (pl. részvényhozamok) volatilitása időben nem állandó. Ennek érzékeltetésére 21 napos gördülő ablakban számítjuk ki a loghozamok szórását, amely egy empirikus becslése a napi volatilitásnak. Az így kapott sorozat jól szemlélteti a piaci turbulencia időbeli változását.
Ez az ábra lehetővé teszi: - volatilitási klaszterek vizsgálatát (pl. válsághelyzetek körül), - a heteroszkedaszticitás vizuális felismerését, - és megalapozza a GARCH-modellek használatát, amelyek a volatilitás időbeli változását formalizálják.
squared_return <- diff_log_returns^2
Lassú lecsengés, fokozatos csökkenés. GARCH struktúra (GARCH komponens, azaz p ≥ 1).
GARCH(1,1) Alapmodell, ha gyorsan csökken az ACF GARCH(1,2) Ha PACF-ben több lag is szignifikáns GARCH(2,1) Ha ACF-ben 2–3 lag is jelentős marad GARCH(2,2) Ha mindkét sorozatban több szignifikáns érték látható.
A legkisebb az ideális választás.
##
## Akaike -6.455461
## Bayes -6.423736
## Shibata -6.455531
## Hannan-Quinn -6.443645
##
## Akaike -6.454135
## Bayes -6.418885
## Shibata -6.454222
## Hannan-Quinn -6.441007
##
## Akaike -6.454841
## Bayes -6.419591
## Shibata -6.454928
## Hannan-Quinn -6.441713
##
## Akaike -6.454540
## Bayes -6.415765
## Shibata -6.454645
## Hannan-Quinn -6.440099
Az első ábra a GARCH(1,1) modell által előrejelzett napi szórást (volatilitást) mutatja a következő 10 napra: - A volatilitás fokozatosan emelkedik, amely a modell szerint növekvő piaci bizonytalanságot jelez. - A sztochasztikus volatilitás nem állandó: a modell az eddigi klasztereződő viselkedést extrapolálja. - Az előrejelzett volatilitás értéke a vizsgált időszak végére meghaladja a 0.011-es szintet, ami viszonylag nyugodt piaci környezetet jelez, de enyhe emelkedő trenddel.
A második ábra az előrejelzett S&P 500 indexárfolyamot mutatja, a GARCH(1,1) modell által becsült hozamok kumulálásával: - A pálya folyamatos emelkedést mutat, ami a modellbe ágyazott pozitív átlagos loghozam következménye. - A volatilitás emelkedése ellenére az árfolyam-trend stabilan felfelé mutat, ami bullish piaci várakozást tükröz. - Az előrejelzés determinisztikusnak tűnik, de ez a GARCH modell sajátossága: csak a szórás időbeli változását modellezi, nem pedig az árfolyam véletlenszerű alakulását (nincs szimulációs elágazás).
A szezonális vizsgálat célja annak feltárása, hogy az S&P 500 log-árfolyama mutat-e ismétlődő mintázatokat évszakosan – azaz például van-e rendszeres emelkedés vagy csökkenés bizonyos hónapokban.
Kétféle szezonális ábrát készítünk:
A ggseasonplot() függvény a forecast csomag része. Egy idősort (itt: log-záróár) évenként színezve jelenít meg úgy, hogy az éven belüli időpontokat (pl. hónapokat) egymás mellé teszi. Ez lehetővé teszi, hogy vizuálisan összehasonlítsuk az egyes évek szezonális mintáit.
A szkript célja, hogy megvizsgálja, hogyan reagáltak az S&P 500 loghozamai a 2022–2023 közötti FOMC kamatdöntések környékén. Minden meeting köré ±10 napos ablakot rajzolunk, majd ezeket összeátlagoljuk. A diagramon látható az átlagos napi hozam a meeting előtt és után, valamint egy ±1 szórású szalag, amely a bizonytalanságot (volatilitást) jelzi.
| Kimenet neve | Jelentése |
|---|---|
| $n_events | 16 → 16 db FOMC meeting dátum szerepel az
elemzésben |
| $n_values | 160 → 16 meeting × 10 napos időablak (pl. +1 … +10
nap), összesen 160 loghozam |
| $sample_mean | 0.001016841 → az eseményt követő loghozamok átlaga |
| $reference_mean | 0.000564307 → a vizsgált időszakon kívüli (baseline)
átlagos hozam |
| $t_test | egymintás t-próba eredménye, amely azt vizsgálja, hogy az esemény utáni hozamok átlaga szignifikánsan eltér-e a referenciaértéktől (0.0005643) |
## $n_events
## [1] 16
##
## $n_values
## [1] 160
##
## $sample_mean
## [1] 0.001016841
##
## $reference_mean
## [1] 0.000564307
##
## $t_test
##
## One Sample t-test
##
## data: post_event_returns
## t = 0.4405, df = 159, p-value = 0.6602
## alternative hypothesis: true mean is not equal to 0.000564307
## 95 percent confidence interval:
## -0.001012097 0.003045779
## sample estimates:
## mean of x
## 0.001016841
A FOMC eseményeket megelőző és követő időszakok volatilitása statisztikailag nem különbözik egymástól. A változás átlaga gyakorlatilag nulla, és a szórás is túl nagy ahhoz, hogy következtetést vonjunk le. Ez alapján nem tudjuk bizonyítani, hogy a FOMC meetingek volatilitásnövekedést váltanak ki az S&P 500 hozamában.
summary_vol <- event_volatility_analysis(diff_log_returns, fomc_dates, window = 40)
summary_vol_clean <- summary_vol %>% filter(!is.na(VolBefore) & !is.na(VolAfter))
# Párosított t-próba a volatilitásokra
t.test(summary_vol_clean$VolBefore, summary_vol_clean$VolAfter, paired = TRUE)
##
## Paired t-test
##
## data: summary_vol_clean$VolBefore and summary_vol_clean$VolAfter
## t = 0.17494, df = 8, p-value = 0.8655
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
## -0.002806789 0.003267622
## sample estimates:
## mean difference
## 0.0002304162
A Non-Farm Payrolls (NFP) az egyik legfontosabb makrogazdasági mutató az Egyesült Államokban, amely a mezőgazdaságon kívüli szektorokban létrejött új munkahelyek számát mutatja. Ezt a mutatót a U.S. Bureau of Labor Statistics (BLS) teszi közzé minden hónap első péntekén, és a munkaerőpiac állapotára, valamint a gazdaság növekedési ütemére utal.
A pénzügyi piacok számára az NFP adat: - jelentős volatilitás-kiváltó esemény, - közvetlenül befolyásolhatja a kamatvárakozásokat (FED politikáján keresztül), - ezáltal hatást gyakorolhat a részvénypiacokra, kötvénypiacokra és devizaárfolyamokra is.
## $n_events
## [1] 84
##
## $n_values
## [1] 700
##
## $sample_mean
## [1] 0.0001058777
##
## $reference_mean
## [1] 0.000564307
##
## $t_test
##
## One Sample t-test
##
## data: post_event_returns
## t = -0.87347, df = 699, p-value = 0.3827
## alternative hypothesis: true mean is not equal to 0.000564307
## 95 percent confidence interval:
## -0.0009245699 0.0011363253
## sample estimates:
## mean of x
## 0.0001058777
A 2019 és 2025 között vizsgált 84 NFP publikáció alapján nem figyelhető meg szignifikáns eseményhatás az amerikai részvénypiacon. Az eseményt követő 10 napos loghozamok átlaga nem tért el szignifikánsan a piaci referenciahozamtól. Ez alapján a NFP jelentések nem jártak együtt kiemelkedő árfolyamreakcióval.
A 2019–2025 közötti 39 NFP esemény körül végzett volatilitás-elemzés alapján nincs statisztikailag szignifikáns bizonyíték arra, hogy a volatilitás megváltozik ezeken a napokon. Bár az átlagos utólagos volatilitás enyhén alacsonyabb, ez a különbség nem elég nagy vagy konzisztens ahhoz, hogy érdemi következtetést vonjunk le.
summary_vol <- event_volatility_analysis(diff_log_returns, nfp_dates, window = 40)
summary_vol_clean <- summary_vol %>% filter(!is.na(VolBefore) & !is.na(VolAfter))
# Párosított t-próba a volatilitásokra
t.test(summary_vol_clean$VolBefore, summary_vol_clean$VolAfter, paired = TRUE)
##
## Paired t-test
##
## data: summary_vol_clean$VolBefore and summary_vol_clean$VolAfter
## t = -1.0974, df = 38, p-value = 0.2794
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
## -0.009363050 0.002780294
## sample estimates:
## mean difference
## -0.003291378